package high.concurrent.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import high.concurrent.config.LockProductMQConfig;
import high.concurrent.dao.ProductMapper;
import high.concurrent.dao.ProductTaskMapper;
import high.concurrent.enums.BizCodeEnum;
import high.concurrent.enums.LockTaskEnum;
import high.concurrent.enums.OrderStateEnum;
import high.concurrent.exception.BizException;
import high.concurrent.feign.OrderFeignService;
import high.concurrent.model.ProductDO;
import high.concurrent.model.ProductTaskDO;
import high.concurrent.model.TasklMessage;
import high.concurrent.request.LockProductRequest;
import high.concurrent.request.ProductItemRequest;
import high.concurrent.service.ProductService;
import high.concurrent.utils.JsonData;
import high.concurrent.vo.ProductVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ProductServiceImpl implements ProductService {

    @Autowired(required = false)
    private ProductMapper productMapper;

    @Autowired(required = false)
    private ProductTaskMapper productTaskMapper;

    @Autowired(required = false)
    private OrderFeignService orderFeignService;

    @Autowired
    private RabbitTemplate rabbitTemplate;
    /**
     * 商品列表查询: 注意返回商品库存需要库存-锁定库存
     * @param page    当前页数
     * @param size    每页条数
     * @return
     */
    @Override
    public JsonData selectProductList(Integer page, Integer size) {

        // 创建分页对象
        Page<ProductDO> pageInfo = new Page<>(page, size);

        // 分页查询商品
        Page<ProductDO> listProductDOs = productMapper.selectPage(pageInfo, new QueryWrapper<ProductDO>()
                .orderByDesc("lock_stock"));

        // 创建Map来存储需要的信息
        Map<String, Object> pageMap = new HashMap<>();
        // 总条数
        pageMap.put("total_record", listProductDOs.getTotal());
        // 总页数
        pageMap.put("total_page", listProductDOs.getPages());
        // 查询的数据,并转成VO返回给前端
        pageMap.put("current_data", listProductDOs.getRecords().stream()
                .map(productDO ->productDoToVo(productDO))
                .collect(Collectors.toList()));

        return JsonData.buildSuccess(pageMap);
    }


    /**
     * 商品详情
     * @param productId    商品id
     * @return
     */
    @Override
    public JsonData getProductDetail(Long productId) {

        // 根据商品ID查询商品详情
        ProductDO productDO = productMapper.selectById(productId);

        // 转型VO并返回
        return JsonData.buildSuccess(productDoToVo(productDO));
    }

    /**
     * 多个商品ID查询对应多个商品信息
     * @param productList  商品id集合
     * @return
     */
    @Override
    public List<ProductVO> selectInProductMsg(List<Long> productList) {

        // 批量查询商品id
        List<ProductDO> listPorductDOs = productMapper.selectList(new QueryWrapper<ProductDO>()
                .in("id", productList));

        // 把DO转成VO
        List<ProductVO> listProductVOs = listPorductDOs.stream()
                .map(productDO -> productDoToVo(productDO))
                .collect(Collectors.toList());

        return listProductVOs;
    }

    /**
     * 购物车所有商品项锁定
     * 1. 提取购物车中所有的商品id
     * 2. 把这些id对应的商品信息全部查出来
     * 3. 扣减商品库存
     * 4. 向库存冻结任务表插入数据
     * 5. 发送延迟消息
     * @param lockProductRequest
     * @return
     */
    @Override
    public JsonData lockProd(LockProductRequest lockProductRequest) {

        // 提取订单号，及商品项中的商品集合(id, buyNum)
        String outTradeNo = lockProductRequest.getOutTradeNo();
        List<ProductItemRequest> productItemRequestList = lockProductRequest.getProductItemRequestList();

        // 提取购物车中所有的商品id
        List<Long> priductIds = productItemRequestList.stream().map(ProductItemRequest::getProductId).collect(Collectors.toList());

        // 遍历出这些id对应的商品信息
        List<ProductVO> productVOS = this.selectInProductMsg(priductIds);

        // 按照productId进行分组， 主键id作为key，对象作为value
        Map<Long, ProductVO> productGroup = productVOS.stream().collect(Collectors.toMap(ProductVO::getId, Function.identity()));

        // 逐个商品扣减库存
        productItemRequestList.stream().forEach(item ->{
            Map<String, Object> mapParam = new HashMap<>();
            mapParam.put("productId", item.getProductId());
            mapParam.put("buyNum", item.getBuyNum());
            int rows = productMapper.rudectProductNum(mapParam);
            if(rows == 1){

                // 扣减库存成功，则向库存冻结任务表插入一条数据
                ProductTaskDO productTaskDO = new ProductTaskDO();
                productTaskDO.setBuyNum(item.getBuyNum());
                productTaskDO.setCreateTime(new Date());
                productTaskDO.setLockState(LockTaskEnum.LOCK.name());
                productTaskDO.setOutTradeNo(outTradeNo);
                productTaskDO.setProductId(item.getProductId());
                productTaskDO.setProductName(productGroup.get(item.getProductId()).getTitle());
                productTaskMapper.insert(productTaskDO);

                // 发送延迟消息
                TasklMessage tasklMessage = new TasklMessage();
                tasklMessage.setOutTradeNo(outTradeNo);
                tasklMessage.setTaskId(productTaskDO.getId());
                rabbitTemplate.convertAndSend(LockProductMQConfig.EXCHANGE_NAME, LockProductMQConfig.DELAY_ROUTING_KEY, tasklMessage);

            }else{
                // 库存锁定失败
                log.info("库存锁定失败: {}", lockProductRequest.toString());
                throw new BizException(BizCodeEnum.ORDER_CONFIRM_LOCK_PRODUCT_FAIL);
            }
        });
        return JsonData.buildSuccess();
    }

    /**
     * 校验商品是否需要回退
     * 1. 判断该任务信息是否存在
     * 2. 查询并判断订单是否存在
     * 3. 订单存在，状态为NEW，则消息重入队列，不做改动
     * 4. 订单存在，状态为PAY，则确认消息，更改任务表状态为FINISH
     * 5. 订单为CANCEL，或者查询订单时崩溃，订单不存在，则库存退还，并更改任务表状态为CANCEL
     * @param productMessage
     * @return
     */
    @Override
    public boolean checkProductTask(TasklMessage productMessage) {

        // 获取【订单id】及【taskId】
        String outTradeNo = productMessage.getOutTradeNo();
        Long taskId = productMessage.getTaskId();

        // 查询该任务，并判断任务是否存在
        ProductTaskDO productTaskDO = productTaskMapper.selectOne(new QueryWrapper<ProductTaskDO>().eq("id", taskId));
        if(productTaskDO == null){
            log.info("该任务id数据不存在: {}", productMessage.toString());
            return true;
        }

        // 冻结任务只有是LOCK状态的才做处理
        if(productTaskDO.getLockState().equals(LockTaskEnum.LOCK)) {
            // 查询订单并判断订单是否存在
            JsonData jsonData = orderFeignService.searchOrderStatus(outTradeNo);
            if (jsonData.getCode() == 0) {
                // 订单存在，状态为NEW，则消息重入队列，不做改动
                if (OrderStateEnum.NEW.name().equals(jsonData.getData())) {
                    log.info("订单还处于创建中，该消息重入队列: {}", jsonData.getData());
                    return false;
                } else if (OrderStateEnum.PAY.name().equals(jsonData.getData())) {
                    // 订单存在，状态为PAY，则确认消息，更改任务表状态为FINISH
                    log.info("订单完成，确认队列， 并更改任务状态为FINISH: {}", jsonData.getData());
                    productTaskDO.setLockState(LockTaskEnum.FINISH.name());
                    productTaskMapper.update(productTaskDO, new QueryWrapper<ProductTaskDO>().eq("id", productMessage.getTaskId()));
                    return true;
                }
            }

            // 订单为CANCEL，或者查询订单时崩溃，订单不存在，【更改任务表状态为CANCEL】
            productTaskDO.setLockState(LockTaskEnum.CANCEL.name());
            productTaskMapper.update(productTaskDO, new QueryWrapper<ProductTaskDO>().eq("id", productMessage.getTaskId()));

            // 归还库存
            Map<String, Object> mapParam = new HashMap<>();
            mapParam.put("productId", productTaskDO.getProductId());
            mapParam.put("buyNum", productTaskDO.getBuyNum());
            productMapper.rtnLockStock(mapParam);
        }

        log.info("订单不存在，或者被取消，或者异常,直接确认消息: {}", productMessage.toString());
        return true;
    }

    /**
     * 商品的DO 转成 VO
     * @param productDO  DO对象
     * @return
     */
    private ProductVO productDoToVo(ProductDO productDO){
        ProductVO productVO = new ProductVO();
        BeanUtils.copyProperties(productDO, productVO);
        // 计算剩余库存
        productVO.setSurplusStock(productVO.getStock() - productVO.getLockStock());

        return productVO;
    }
}
